import SwiftSyntax
import SwiftSyntaxMacros

/// A type-erased `EnumSwitcherVariable` value.
///
/// The `AnyEnumSwitcher` type forwards `EnumSwitcherVariable`
/// implementations to an underlying variable value, hiding the type of the
/// wrapped value.
struct AnyEnumSwitcher: EnumSwitcherVariable {
    /// The value wrapped by this instance.
    ///
    /// The base property can be cast back
    /// to its original type using type casting
    /// operators (`as?`, `as!`, or `as`).
    let base: any EnumSwitcherVariable

    /// Provides node at which case associated variables are registered.
    ///
    /// Provides node provided by the underlying variable value.
    ///
    /// - Parameters:
    ///   - decl: The declaration for which to provide.
    ///   - context: The context in which to perform the macro expansion.
    ///
    /// - Returns: The registering node.
    func node(
        for decl: EnumCaseVariableDeclSyntax,
        in context: some MacroExpansionContext
    ) -> PropertyVariableTreeNode {
        return base.node(for: decl, in: context)
    }

    /// Creates value expressions for provided enum-case variable.
    ///
    /// Provides value generated by the underlying variable value.
    ///
    /// - Parameters:
    ///   - variable: The variable for which generated.
    ///   - values: The values present in syntax.
    ///   - codingKeys: The map where `CodingKeys` maintained.
    ///   - context: The context in which to perform the macro expansion.
    ///
    /// - Returns: The generated value.
    func keyExpression<Var: EnumCaseVariable>(
        for variable: Var, values: [ExprSyntax],
        codingKeys: CodingKeysMap, context: some MacroExpansionContext
    ) -> EnumVariable.CaseValue {
        return base.keyExpression(
            for: variable, values: values,
            codingKeys: codingKeys, context: context
        )
    }

    /// Update provided variable data.
    ///
    /// Passes provided variable to underlying variable value for update.
    ///
    /// - Parameter variable: The variable to transform.
    /// - Returns: Transformed variable.
    func transform(
        variable: BasicAssociatedVariable
    ) -> BasicAssociatedVariable {
        return base.transform(variable: variable)
    }

    /// Provides the syntax for decoding this variable at the provided location.
    ///
    /// Provides syntax for decoding of the underlying variable value.
    ///
    /// - Parameters:
    ///   - context: The context in which to perform the macro expansion.
    ///   - location: The decoding location for the variable.
    ///
    /// - Returns: The generated variable decoding syntax.
    func decoding(
        in context: some MacroExpansionContext,
        from location: EnumSwitcherLocation
    ) -> CodeBlockItemListSyntax {
        return base.decoding(in: context, from: location)
    }

    /// Provides the syntax for encoding this variable at the provided location.
    ///
    /// Provides syntax for encoding of the underlying variable value.
    ///
    /// - Parameters:
    ///   - context: The context in which to perform the macro expansion.
    ///   - location: The encoding location for the variable.
    ///
    /// - Returns: The generated variable encoding syntax.
    func encoding(
        in context: some MacroExpansionContext,
        to location: EnumSwitcherLocation
    ) -> CodeBlockItemListSyntax {
        return base.encoding(in: context, to: location)
    }

    /// Creates additional enum declarations for enum variable.
    ///
    /// Provides enum declarations of the underlying variable value.
    ///
    /// - Parameter context: The context in which to perform the macro
    ///   expansion.
    /// - Returns: The generated enum declaration syntax.
    func codingKeys(
        in context: some MacroExpansionContext
    ) -> MemberBlockItemListSyntax {
        return base.codingKeys(in: context)
    }
}

extension EnumSwitcherVariable {
    /// Erase type of this variable.
    ///
    /// Wraps this variable in an `AnyEnumSwitcher` instance.
    /// The implementation stays unchanged while type is erased.
    var any: AnyEnumSwitcher {
        return .init(base: self)
    }
}
